home *** CD-ROM | disk | FTP | other *** search
/ Aminet 45 / Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso / Aminet / gfx / x11 / x3270_3_2_16.lha / amiga_src / keymap.c < prev    next >
C/C++ Source or Header  |  2001-06-23  |  29KB  |  1,231 lines

  1. /*
  2.  * Copyright 1996, 1999, 2000 by Paul Mattes.
  3.  *  Permission to use, copy, modify, and distribute this software and its
  4.  *  documentation for any purpose and without fee is hereby granted,
  5.  *  provided that the above copyright notice appear in all copies and that
  6.  *  both that copyright notice and this permission notice appear in
  7.  *  supporting documentation.
  8.  */
  9.  
  10. /*
  11.  *    keymap.c
  12.  *        This module handles keymaps.
  13.  */
  14.  
  15. #include "globals.h"
  16. #include <X11/IntrinsicP.h>
  17. #include <X11/Shell.h>
  18. #include <X11/StringDefs.h>
  19. #include <X11/Xaw/Command.h>
  20. #include <X11/Xaw/Form.h>
  21. #include <X11/Xaw/Label.h>
  22. #include <X11/Xaw/Text.h>
  23. #include <X11/Xaw/AsciiText.h>
  24. #include <X11/Xaw/AsciiSrc.h>
  25. #include <errno.h>
  26. #include "appres.h"
  27. #include "objects.h"
  28. #include "resources.h"
  29.  
  30. #include "hostc.h"
  31. #include "keymapc.h"
  32. #include "keypadc.h"
  33. #include "kybdc.h"
  34. #include "popupsc.h"
  35. #include "screenc.h"
  36. #include "statusc.h"
  37. #include "utilc.h"
  38.  
  39. #define PA_ENDL        " " PA_END "()"
  40.  
  41. Boolean keymap_changed = False;
  42. struct trans_list *trans_list = (struct trans_list *)NULL;
  43. static struct trans_list **last_trans = &trans_list;
  44. static struct trans_list *tkm_last;
  45. struct trans_list *temp_keymaps;    /* temporary keymap list */
  46. char *keymap_trace = CN;
  47. static char *last_keymap = CN;
  48. static Boolean last_nvt = False;
  49.  
  50. static void setup_keymaps(const char *km, Boolean do_popup);
  51. static void add_keymap(const char *name, Boolean do_popup);
  52. static void add_trans(const char *name, char *translations, char *pathname,
  53.     Boolean is_from_server);
  54. static char *get_file_keymap(const char *name, char **pathp);
  55. static void keymap_3270_mode(Boolean);
  56.  
  57. /* Undocumented Xt function to convert translations to text. */
  58. extern String _XtPrintXlations(Widget w, XtTranslations xlations,
  59.     Widget accelWidget, Boolean includeRHS);
  60.  
  61. extern Widget *screen;
  62.  
  63. #if defined(X3270_MENUS) /*[*/
  64. extern Pixmap diamond;
  65. extern Pixmap no_diamond;
  66.  
  67. static enum { SORT_EVENT, SORT_KEYMAP, SORT_ACTION } sort = SORT_KEYMAP;
  68.  
  69. static Boolean km_isup = False;
  70. static Widget km_shell, sort_event, sort_keymap, sort_action, text;
  71. static char km_file[128];
  72. static void create_text(void);
  73. static void km_up(Widget w, XtPointer client_data, XtPointer call_data);
  74. static void km_down(Widget w, XtPointer client_data, XtPointer call_data);
  75. static void km_done(Widget w, XtPointer client_data, XtPointer call_data);
  76. static void do_sort_action(Widget w, XtPointer client_data,
  77.     XtPointer call_data);
  78. static void do_sort_keymap(Widget w, XtPointer client_data,
  79.     XtPointer call_data);
  80. static void do_sort_event(Widget w, XtPointer client_data, XtPointer call_data);
  81. static void format_xlations(String s, FILE *f);
  82. static int action_cmp(char *s1, char *s2);
  83. static int keymap_cmp(char *k1, int l1, char *k2, int l2);
  84. static int event_cmp(char *e1, char *e2);
  85. static Boolean is_temp(char *k);
  86. static char *pathname(char *k);
  87. static Boolean from_server(char *k);
  88. static void km_regen(void);
  89. #else /*][*/
  90. #define km_regen()
  91. #endif /*]*/
  92.  
  93. /* Keymap initialization. */
  94. void
  95. keymap_init(const char *km)
  96. {
  97.     static Boolean initted = False;
  98.  
  99.     if (km == CN)
  100.         if ((km = (char *)getenv("KEYMAP")) == CN)
  101.             if ((km = (char *)getenv("KEYBD")) == CN)
  102.                 km = "@server";
  103.     setup_keymaps(km, initted);
  104.     if (!initted) {
  105.         initted = True;
  106.         last_nvt = IN_ANSI;
  107.         register_schange(ST_3270_MODE, keymap_3270_mode);
  108.         register_schange(ST_CONNECT, keymap_3270_mode);
  109.     } else {
  110.         screen_set_keymap();
  111.         keypad_set_keymap();
  112.     }
  113.     km_regen();
  114.  
  115.     /* Save the name(s) of the last keymap, so we can switch modes later. */
  116.     if (km != last_keymap) {
  117.         if (last_keymap != CN) {
  118.             Free(last_keymap);
  119.             last_keymap = CN;
  120.         }
  121.         if (km != CN)
  122.             last_keymap = NewString(km);
  123.     }
  124. }
  125.  
  126. /*
  127.  * 3270/NVT mode change.
  128.  */
  129. static void
  130. keymap_3270_mode(Boolean ignored unused)
  131. {
  132.     if (last_nvt != IN_ANSI) {
  133.         last_nvt = IN_ANSI;
  134.         keymap_init(last_keymap);
  135.     }
  136. }
  137.  
  138. /*
  139.  * Set up a user keymap.
  140.  */
  141. static void
  142. setup_keymaps(const char *km, Boolean do_popup)
  143. {
  144.     Boolean saw_apl_keymod = False;
  145.     struct trans_list *t;
  146.     struct trans_list *next;
  147.  
  148.     /* Make sure it starts with "base". */
  149.     if (km == CN)
  150.         km = "base";
  151.     else {
  152.         char *k = XtMalloc(5 + strlen(km) + 1);
  153.  
  154.         (void) sprintf(k, "base,%s", km);
  155.         km = k;
  156.     }
  157.  
  158.     if (do_popup)
  159.         keymap_changed = True;
  160.  
  161.     /* Clear out any existing translations. */
  162.     appres.key_map = (char *)NULL;
  163.     for (t = trans_list; t != (struct trans_list *)NULL; t = next) {
  164.         next = t->next;
  165.         XtFree(t->name);
  166.         if (t->pathname != CN)
  167.             XtFree(t->pathname);
  168.         XtFree((char *)t);
  169.     }
  170.     trans_list = (struct trans_list *)NULL;
  171.     last_trans = &trans_list;
  172.  
  173.     /* Build up the new list. */
  174.     if (km != CN) {
  175.         char *ns = XtNewString(km);
  176.         char *n0 = ns;
  177.         char *comma;
  178.  
  179.         do {
  180.             comma = strchr(ns, ',');
  181.             if (comma)
  182.                 *comma = '\0';
  183.             if (!strcmp(ns, Apl))
  184.                 saw_apl_keymod = True;
  185.             add_keymap(ns, do_popup);
  186.             if (comma)
  187.                 ns = comma + 1;
  188.             else
  189.                 ns = NULL;
  190.         } while (ns);
  191.  
  192.         XtFree(n0);
  193.     }
  194.     if (appres.apl_mode && !saw_apl_keymod)
  195.         add_keymap(Apl, do_popup);
  196. }
  197.  
  198. /*
  199.  * Get a keymap from a file.
  200.  */
  201. static char *
  202. get_file_keymap(const char *name, char **pathp)
  203. {
  204. #if 0
  205.     char *home;
  206. #endif
  207.     char *path;
  208.     XrmDatabase dd = (XrmDatabase)NULL;
  209.     char *resname;
  210.     XrmValue value;
  211.     char *type;
  212.     char *r = CN;
  213.  
  214.     *pathp = CN;
  215.  
  216. #if 0
  217.     /* Look for a user's keymap file. */
  218.     home = getenv("HOME");
  219.     if (home != CN) {
  220.         path = xs_buffer("%s/.x3270/keymap.%s", home, name);
  221.         dd = XrmGetFileDatabase(path);
  222.         if (dd != (XrmDatabase)NULL)
  223.             *pathp = path;
  224.         else
  225.             XtFree(path);
  226.     }
  227. #endif
  228.  
  229.     /* Look for a global keymap file. */
  230.     if (dd == (XrmDatabase)NULL) {
  231.         path = xs_buffer("%s/keymap.%s", LIBX3270DIR, name);
  232.         dd = XrmGetFileDatabase(path);
  233.         if (dd != (XrmDatabase)NULL)
  234.             *pathp = path;
  235.         else {
  236.             XtFree(path);
  237.             return CN;
  238.         }
  239.     }
  240.  
  241.     /* Look up the resource in that file. */
  242.     resname = xs_buffer("%s.%s.%s", XtName(toplevel), ResKeymap, name);
  243.     if ((XrmGetResource(dd, resname, 0, &type, &value) == True) &&
  244.             *value.addr) {
  245.         r = XtNewString(value.addr);
  246.     } else {
  247.         *pathp = CN;
  248.     }
  249.     XtFree(resname);
  250.     XrmDestroyDatabase(dd);
  251.     return r;
  252. }
  253.  
  254. /*
  255.  * Add to the list of user-specified keymap translations, finding both the
  256.  * system and user versions of a keymap.
  257.  */
  258. static void
  259. add_keymap(const char *name, Boolean do_popup)
  260. {
  261.     char *translations, *translations_nvt;
  262.     char *buf, *buf_nvt;
  263.     int any = 0;
  264.     char *path, *path_nvt;
  265.     Boolean is_from_server = False;
  266.  
  267.     if (appres.key_map == (char *)NULL)
  268.         appres.key_map = XtNewString(name);
  269.     else {
  270.         char *t = xs_buffer("%s,%s", appres.key_map, name);
  271.  
  272.         XtFree(appres.key_map);
  273.         appres.key_map = t;
  274.     }
  275.  
  276.     /* Translate '@server' to a vendor-specific keymap. */
  277.     if (!strcmp(name, "@server")) {
  278.         struct sk {
  279.             struct sk *next;
  280.             char *vendor;
  281.             char *keymap;
  282.         };
  283.         static struct sk *sk_list = (struct sk *)NULL;
  284.         struct sk *sk;
  285.  
  286.         if (sk_list == (struct sk *)NULL) {
  287.             char *s, *vendor, *keymap;
  288.  
  289.             s = get_resource("serverKeymapList");
  290.             if (s == CN)
  291.                 return;
  292.             s = XtNewString(s);
  293.             while (split_dresource(&s, &vendor, &keymap) == 1) {
  294.                 sk = (struct sk *)XtMalloc(sizeof(struct sk));
  295.                 sk->vendor = vendor;
  296.                 sk->keymap = keymap;
  297.                 sk->next = sk_list;
  298.                 sk_list = sk;
  299.             }
  300.         }
  301.         for (sk = sk_list; sk != (struct sk *)NULL; sk = sk->next) {
  302.             if (!strcmp(sk->vendor, ServerVendor(display))) {
  303.                 name = sk->keymap;
  304.                 is_from_server = True;
  305.                 break;
  306.             }
  307.         }
  308.         if (sk == (struct sk *)NULL)
  309.             return;
  310.     }
  311.  
  312.     /* Try for a file first, then resources. */
  313.     translations = get_file_keymap(name, &path);
  314.     buf_nvt = xs_buffer("%s.%s", name, ResNvt);
  315.     translations_nvt = get_file_keymap(buf_nvt, &path_nvt);
  316.     if (translations != CN || translations_nvt != CN) {
  317.         any++;
  318.         if (IN_ANSI && translations_nvt != CN)
  319.             add_trans(buf_nvt, translations_nvt, path_nvt,
  320.                 is_from_server);
  321.         else if (translations != CN)
  322.             add_trans(name, translations, path, is_from_server);
  323.         if (translations != CN)
  324.             XtFree(translations);
  325.         if (translations_nvt != CN)
  326.             XtFree(translations_nvt);
  327.         XtFree(buf_nvt);
  328.     } else {
  329.         XtFree(buf_nvt);
  330.  
  331.         /* Shared keymap. */
  332.         buf = xs_buffer("%s.%s", ResKeymap, name);
  333.         translations = get_resource(buf);
  334.         buf_nvt = xs_buffer("%s.%s.%s", ResKeymap, name, ResNvt);
  335.         translations_nvt = get_resource(buf_nvt);
  336.         if (translations != CN || translations_nvt != CN)
  337.             any++;
  338.         if (IN_ANSI && translations_nvt != CN)
  339.             add_trans(buf_nvt + strlen(ResKeymap) + 1,
  340.                 translations_nvt, CN, is_from_server);
  341.         else if (translations != CN)
  342.             add_trans(name, translations, CN, is_from_server);
  343.         XtFree(buf);
  344.         XtFree(buf_nvt);
  345.  
  346.         /* User keymap */
  347.         buf = xs_buffer("%s.%s.%s", ResKeymap, name, ResUser);
  348.         translations = get_resource(buf);
  349.         buf_nvt = xs_buffer("%s.%s.%s.%s", ResKeymap, name, ResNvt,
  350.             ResUser);
  351.         translations_nvt = get_resource(buf_nvt);
  352.         if (translations != CN || translations_nvt != CN)
  353.             any++;
  354.         if (IN_ANSI && translations_nvt != CN)
  355.             add_trans(buf_nvt + strlen(ResKeymap) + 1,
  356.                 translations_nvt, CN, is_from_server);
  357.         else if (translations != CN)
  358.             add_trans(buf, translations, CN, is_from_server);
  359.         XtFree(buf);
  360.         XtFree(buf_nvt);
  361.     }
  362.  
  363.     if (!any) {
  364.         if (do_popup)
  365.             popup_an_error("Cannot find %s \"%s\"", ResKeymap,
  366.                 name);
  367.         else
  368.             xs_warning("Cannot find %s \"%s\"", ResKeymap, name);
  369.     }
  370. }
  371.  
  372. /*
  373.  * Add a single keymap name and translation to the translation list.
  374.  */
  375. static void
  376. add_trans(const char *name, char *translations, char *path_name,
  377.     Boolean is_from_server)
  378. {
  379.     struct trans_list *t;
  380.  
  381.     t = (struct trans_list *)XtMalloc(sizeof(*t));
  382.     t->name = XtNewString(name);
  383.     t->pathname = path_name;
  384.     t->is_temp = False;
  385.     t->from_server = is_from_server;
  386.     (void) lookup_tt(name, translations);
  387.     t->next = NULL;
  388.     *last_trans = t;
  389.     last_trans = &t->next;
  390. }
  391.  
  392. /*
  393.  * Translation table expansion.
  394.  */
  395.  
  396. /* Find the first unquoted newline an an action list. */
  397. static char *
  398. unquoted_newline(char *s)
  399. {
  400.     Boolean bs = False;
  401.     enum { UQ_BASE, UQ_PLIST, UQ_Q } state = UQ_BASE;
  402.     char c;
  403.  
  404.     for ( ; (c = *s); s++) {
  405.         if (bs) {
  406.             bs = False;
  407.             continue;
  408.         } else if (c == '\\') {
  409.             bs = True;
  410.             continue;
  411.         }
  412.         switch (state) {
  413.             case UQ_BASE:
  414.             if (c == '(')
  415.                 state = UQ_PLIST;
  416.             else if (c == '\n')
  417.                 return s;
  418.             break;
  419.             case UQ_PLIST:
  420.             if (c == ')')
  421.                 state = UQ_BASE;
  422.             else if (c == '"')
  423.                 state = UQ_Q;
  424.             break;
  425.             case UQ_Q:
  426.             if (c == '"')
  427.                 state = UQ_PLIST;
  428.             break;
  429.         }
  430.     }
  431.     return CN;
  432. }
  433.  
  434. /* Expand a translation table with keymap tracing calls. */
  435. static char *
  436. expand_table(const char *name, char *table)
  437. {
  438.     char *cm, *t0, *t, *s;
  439.     int nlines = 1;
  440.  
  441.     if (table == CN)
  442.         return CN;
  443.  
  444.     /* Roughly count the number of lines in the table. */
  445.     cm = table;
  446.     while ((cm = strchr(cm, '\n')) != CN) {
  447.         nlines++;
  448.         cm++;
  449.     }
  450.  
  451.     /* Allocate a new buffer. */
  452.     t = t0 = (char *)XtMalloc(2 + strlen(table) +
  453.         nlines * (strlen(" " PA_KEYMAP_TRACE "(,nnnn) ") + strlen(name) +
  454.               strlen(PA_ENDL)));
  455.     *t = '\0';
  456.  
  457.     /* Expand the table into it. */
  458.     s = table;
  459.     nlines = 0;
  460.     while (*s) {
  461.         /* Skip empty lines. */
  462.         while (*s == ' ' || *s == '\t')
  463.             s++;
  464.         if (*s == '\n') {
  465.             *t++ = '\n';
  466.             *t = '\0';
  467.             s++;
  468.             continue;
  469.         }
  470.  
  471.         /* Find the '>' from the event name, and copy up through it. */
  472.         cm = strchr(s, '>');
  473.         if (cm == CN) {
  474.             while ((*t++ = *s++))
  475.                 ;
  476.             break;
  477.         }
  478.         while (s <= cm)
  479.             *t++ = *s++;
  480.  
  481.         /* Find the ':' following, and copy up throught that. */
  482.         cm = strchr(s, ':');
  483.         if (cm == CN) {
  484.             while ((*t++ = *s++))
  485.                 ;
  486.             break;
  487.         }
  488.         nlines++;
  489.         while (s <= cm)
  490.             *t++ = *s++;
  491.  
  492. #if defined(X3270_TRACE) /*[*/
  493.         /* Insert a PA-KeymapTrace call. */
  494.         (void) sprintf(t, " " PA_KEYMAP_TRACE "(%s,%d) ", name, nlines);
  495.         t = strchr(t, '\0');
  496. #endif /*]*/
  497.  
  498.         /*
  499.          * Copy to the next unquoted newline and append a PA-End call.
  500.          */
  501.         cm = unquoted_newline(s);
  502.         if (cm == CN) {
  503.             while ((*t = *s)) {
  504.                 t++;
  505.                 s++;
  506.             }
  507.         } else {
  508.             while (s < cm)
  509.                 *t++ = *s++;
  510.         }
  511. #if defined(X3270_TRACE) /*[*/
  512.         (void) strcpy(t, PA_ENDL);
  513.         t += strlen(PA_ENDL);
  514. #endif /*]*/
  515.         if (cm == CN)
  516.             break;
  517.         else
  518.             *t++ = *s++;
  519.     }
  520.     *t = '\0';
  521.  
  522.     return t0;
  523. }
  524.  
  525. #if defined(X3270_TRACE) /*[*/
  526. /*
  527.  * Trace a keymap.
  528.  *
  529.  * This function leaves a value in the global "keymap_trace", which is used
  530.  * by the action_debug function when subsequent actions are called.
  531.  */
  532. void
  533. PA_KeymapTrace_action(Widget w unused, XEvent *event unused, String *params,
  534.     Cardinal *num_params)
  535. {
  536.     if (!toggled(EVENT_TRACE) || *num_params != 2)
  537.         return;
  538.     if (keymap_trace != CN)
  539.         XtFree((XtPointer)keymap_trace);
  540.     keymap_trace = XtMalloc(strlen(params[0]) + 1 + strlen(params[1]) + 1);
  541.     (void) sprintf(keymap_trace, "%s:%s", params[0], params[1]);
  542. }
  543. #endif /*]*/
  544.  
  545. /*
  546.  * End a keymap trace.
  547.  *
  548.  * This function clears the value in the global "keymap_trace".
  549.  */
  550. void
  551. PA_End_action(Widget w unused, XEvent *event unused, String *params unused,
  552.     Cardinal *num_params unused)
  553. {
  554.     if (keymap_trace != CN)
  555.         XtFree((XtPointer)keymap_trace);
  556.     keymap_trace = CN;
  557. }
  558.  
  559. /*
  560.  * Translation table cache.
  561.  */
  562. XtTranslations
  563. lookup_tt(const char *name, char *table)
  564. {
  565.     struct tt_cache {
  566.         char *name;
  567.         XtTranslations trans;
  568.         struct tt_cache *next;
  569.     };
  570. #    define TTN (struct tt_cache *)NULL
  571.     static struct tt_cache *tt_cache = TTN;
  572.     struct tt_cache *t;
  573.     char *xtable;
  574.  
  575.     /* Look for an old one. */
  576.     for (t = tt_cache; t != TTN; t = t->next)
  577.         if (!strcmp(name, t->name))
  578.             return t->trans;
  579.  
  580.     /* Allocate and translate a new one. */
  581.     t = (struct tt_cache *)XtMalloc(sizeof(*t));
  582.     t->name = XtNewString(name);
  583.     xtable = expand_table(name, table);
  584.     t->trans = XtParseTranslationTable(xtable);
  585.     XtFree((XtPointer)xtable);
  586.     t->next = tt_cache;
  587.     tt_cache = t;
  588.  
  589.     return t->trans;
  590. }
  591. #undef TTN
  592.  
  593. /*
  594.  * Set or clear a temporary keymap.
  595.  *
  596.  * If the parameter is CN, removes all keymaps.
  597.  * Otherwise, toggles the keymap by that name.
  598.  *
  599.  * Returns 0 if the action was successful, -1 otherwise.
  600.  *
  601.  */
  602. int
  603. temporary_keymap(char *k)
  604. {
  605.     char *kmname, *km;
  606.     XtTranslations trans;
  607.     struct trans_list *t, *prev;
  608.     char *path = CN;
  609. #    define TN (struct trans_list *)NULL
  610.  
  611.     if (k == CN) {
  612.         struct trans_list *next;
  613.  
  614.         /* Delete all temporary keymaps. */
  615.         for (t = temp_keymaps; t != TN; t = next) {
  616.             XtFree((XtPointer)t->name);
  617.             if (t->pathname != CN)
  618.                 XtFree((XtPointer)t->pathname);
  619.             next = t->next;
  620.             XtFree((XtPointer)t);
  621.         }
  622.         tkm_last = temp_keymaps = TN;
  623.         screen_set_temp_keymap((XtTranslations)NULL);
  624.         keypad_set_temp_keymap((XtTranslations)NULL);
  625.         status_kmap(False);
  626.         km_regen();
  627.         return 0;
  628.     }
  629.  
  630.     /* Check for deleting one keymap. */
  631.     for (prev = TN, t = temp_keymaps; t != TN; prev = t, t = t->next)
  632.         if (!strcmp(k, t->name))
  633.             break;
  634.     if (t != TN) {
  635.  
  636.         /* Delete the keymap from the list. */
  637.         if (prev != TN)
  638.             prev->next = t->next;
  639.         else
  640.             temp_keymaps = t->next;
  641.         if (tkm_last == t)
  642.             tkm_last = prev;
  643.         XtFree((XtPointer)t->name);
  644.         XtFree((XtPointer)t);
  645.  
  646.         /* Rebuild the translation tables from the remaining ones. */
  647.         screen_set_temp_keymap((XtTranslations)NULL);
  648.         keypad_set_temp_keymap((XtTranslations)NULL);
  649.         for (t = temp_keymaps; t != TN; t = t->next) {
  650.             trans = lookup_tt(t->name, CN);
  651.             screen_set_temp_keymap(trans);
  652.             keypad_set_temp_keymap(trans);
  653.         }
  654.  
  655.         /* Update the status line. */
  656.         if (temp_keymaps == TN)
  657.             status_kmap(False);
  658.         km_regen();
  659.         return 0;
  660.     }
  661.  
  662.     /* Add a keymap. */
  663.  
  664.     /* Try a file first. */
  665.     km = get_file_keymap(k, &path);
  666.     if (km == CN) {
  667.         /* Then try a resource. */
  668.         kmname = xs_buffer("%s.%s", ResKeymap, k);
  669.         km = get_resource(kmname);
  670.         XtFree(kmname);
  671.         if (km == CN)
  672.             return -1;
  673.     }
  674.  
  675.     /* Update the translation tables. */
  676.     trans = lookup_tt(k, km);
  677.     screen_set_temp_keymap(trans);
  678.     keypad_set_temp_keymap(trans);
  679.  
  680.     /* Add it to the list. */
  681.     t = (struct trans_list *)XtMalloc(sizeof(*t));
  682.     t->name = XtNewString(k);
  683.     t->pathname = path;
  684.     t->is_temp = True;
  685.     t->from_server = False;
  686.     t->next = TN;
  687.     if (tkm_last != TN)
  688.         tkm_last->next = t;
  689.     else
  690.         temp_keymaps = t;
  691.     tkm_last = t;
  692.  
  693.     /* Update the status line. */
  694.     status_kmap(True);
  695.     km_regen();
  696.  
  697.     /* Success. */
  698.     return 0;
  699. }
  700. #undef TN
  701.  
  702. #if defined(X3270_MENUS) /*[*/
  703. /* Create and pop up the current keymap pop-up. */
  704. void
  705. do_keymap_display(Widget w unused, XtPointer userdata unused,
  706.     XtPointer calldata unused)
  707. {
  708.     Widget form, label, done;
  709.  
  710.     /* Create the popup. */
  711.     km_shell = XtVaCreatePopupShell(
  712.         "kmPopup", transientShellWidgetClass, toplevel,
  713.         NULL);
  714.     XtAddCallback(km_shell, XtNpopupCallback, place_popup,
  715.         (XtPointer) CenterP);
  716.     XtAddCallback(km_shell, XtNpopupCallback, km_up, (XtPointer)NULL);
  717.     XtAddCallback(km_shell, XtNpopdownCallback, km_down, (XtPointer)NULL);
  718.  
  719.     /* Create a form in the popup. */
  720.     form = XtVaCreateManagedWidget(
  721.         ObjDialog, formWidgetClass, km_shell,
  722.         NULL);
  723.  
  724.     /* Create the title. */
  725.     label = XtVaCreateManagedWidget("label", labelWidgetClass, form,
  726.         XtNborderWidth, 0,
  727.         NULL);
  728.  
  729.     /* Create the options. */
  730.     sort_event = XtVaCreateManagedWidget("sortEventOption",
  731.         commandWidgetClass, form,
  732.         XtNborderWidth, 0,
  733.         XtNfromVert, label,
  734.         XtNleftBitmap, sort == SORT_EVENT ? diamond : no_diamond,
  735.         NULL);
  736.     XtAddCallback(sort_event, XtNcallback, do_sort_event,
  737.         (XtPointer)NULL);
  738.     sort_keymap = XtVaCreateManagedWidget("sortKeymapOption",
  739.         commandWidgetClass, form,
  740.         XtNborderWidth, 0,
  741.         XtNfromVert, sort_event,
  742.         XtNleftBitmap, sort == SORT_KEYMAP ? diamond : no_diamond,
  743.         NULL);
  744.     XtAddCallback(sort_keymap, XtNcallback, do_sort_keymap,
  745.         (XtPointer)NULL);
  746.     sort_action = XtVaCreateManagedWidget("sortActionOption",
  747.         commandWidgetClass, form,
  748.         XtNborderWidth, 0,
  749.         XtNfromVert, sort_keymap,
  750.         XtNleftBitmap, sort == SORT_ACTION ? diamond : no_diamond,
  751.         NULL);
  752.     XtAddCallback(sort_action, XtNcallback, do_sort_action,
  753.         (XtPointer)NULL);
  754.  
  755.     /* Create a text widget attached to the file. */
  756.     text = XtVaCreateManagedWidget(
  757.         "text", asciiTextWidgetClass, form,
  758.         XtNfromVert, sort_action,
  759.         XtNscrollHorizontal, XawtextScrollAlways,
  760.         XtNscrollVertical, XawtextScrollAlways,
  761.         XtNdisplayCaret, False,
  762.         NULL);
  763.     create_text();
  764.  
  765.     /* Create the Done button. */
  766.     done = XtVaCreateManagedWidget(ObjConfirmButton,
  767.         commandWidgetClass, form,
  768.         XtNfromVert, text,
  769.         NULL);
  770.     XtAddCallback(done, XtNcallback, km_done, (XtPointer)NULL);
  771.  
  772.     /* Pop it up. */
  773.     popup_popup(km_shell, XtGrabNone);
  774. }
  775.  
  776. /* Format the keymap into a text source. */
  777. static void
  778. create_text(void)
  779. {
  780.     String s;
  781.     FILE *f;
  782.     Widget source, old;
  783.  
  784.     /* Ready a file. */
  785.     (void) sprintf(km_file, "/tmp/km.%d", getpid());
  786.     f = fopen(km_file, "w");
  787.     if (f == (FILE *)NULL) {
  788.         popup_an_errno(errno, "temporary file open");
  789.         return;
  790.     }
  791.  
  792. #ifndef AMIGA
  793.     s = _XtPrintXlations(*screen, (*screen)->core.tm.translations, NULL,
  794.         True);
  795.     format_xlations(s, f);
  796.     XtFree(s);
  797. #endif
  798.     fclose(f);
  799.     source = XtVaCreateWidget(
  800.         "source", asciiSrcObjectClass, text,
  801.         XtNtype, XawAsciiFile,
  802.         XtNstring, km_file,
  803.         XtNeditType, XawtextRead,
  804.         NULL);
  805.     old = XawTextGetSource(text);
  806.     XawTextSetSource(text, source, (XawTextPosition)0);
  807.     XtDestroyWidget(old);
  808. }
  809.  
  810. /* Refresh the keymap display, if it's up. */
  811. static void
  812. km_regen(void)
  813. {
  814.     if (km_isup)
  815.         create_text();
  816. }
  817.  
  818. /* Popup callback. */
  819. static void
  820. km_up(Widget w unused, XtPointer client_data unused, XtPointer call_data unused)
  821. {
  822.     km_isup = True;
  823. }
  824.  
  825. /* Popdown callback.  Destroy the widget. */
  826. static void
  827. km_down(Widget w unused, XtPointer client_data unused,
  828.     XtPointer call_data unused)
  829. {
  830.     km_isup = False;
  831.     (void) unlink(km_file);
  832.     XtDestroyWidget(km_shell);
  833. }
  834.  
  835. /* Done button callback.  Pop down the widget. */
  836. static void
  837. km_done(Widget w unused, XtPointer client_data unused,
  838.     XtPointer call_data unused)
  839. {
  840.     XtPopdown(km_shell);
  841. }
  842.  
  843. /* "Sort-by-event" button callback. */
  844. static void
  845. do_sort_event(Widget w unused, XtPointer client_data unused,
  846.     XtPointer call_data unused)
  847. {
  848.     if (sort != SORT_EVENT) {
  849.         sort = SORT_EVENT;
  850.         XtVaSetValues(sort_action, XtNleftBitmap, no_diamond, NULL);
  851.         XtVaSetValues(sort_keymap, XtNleftBitmap, no_diamond, NULL);
  852.         XtVaSetValues(sort_event, XtNleftBitmap, diamond, NULL);
  853.         create_text();
  854.     }
  855. }
  856.  
  857. /* "Sort-by-keymap" button callback. */
  858. static void
  859. do_sort_keymap(Widget w unused, XtPointer client_data unused,
  860.     XtPointer call_data unused)
  861. {
  862.     if (sort != SORT_KEYMAP) {
  863.         sort = SORT_KEYMAP;
  864.         XtVaSetValues(sort_action, XtNleftBitmap, no_diamond, NULL);
  865.         XtVaSetValues(sort_keymap, XtNleftBitmap, diamond, NULL);
  866.         XtVaSetValues(sort_event, XtNleftBitmap, no_diamond, NULL);
  867.         create_text();
  868.     }
  869. }
  870.  
  871. /* "Sort-by-action" button callback. */
  872. static void
  873. do_sort_action(Widget w unused, XtPointer client_data unused,
  874.     XtPointer call_data unused)
  875. {
  876.     if (sort != SORT_ACTION) {
  877.         sort = SORT_ACTION;
  878.         XtVaSetValues(sort_action, XtNleftBitmap, diamond, NULL);
  879.         XtVaSetValues(sort_keymap, XtNleftBitmap, no_diamond, NULL);
  880.         XtVaSetValues(sort_event, XtNleftBitmap, no_diamond, NULL);
  881.         create_text();
  882.     }
  883. }
  884.  
  885. #define DASHES \
  886.     "-------------------------- ---------------- ------------------------------------"
  887.  
  888. /*
  889.  * Format translations for display.
  890.  *
  891.  * The data from _XtPrintXlations looks like this:
  892.  *  [<space>]event:<space>[PA-KeymapTrace("keymap","line")<space>][action...]
  893.  * with the delightful complication that embedded quotes are not quoted.
  894.  *
  895.  * What we want to do is to:
  896.  *  remove all lines without PA-KeymapTrace
  897.  *  remove the leading space
  898.  *  sort by actions list
  899.  *  reformat as:
  900.  *    action... event (keymap:line)
  901.  */
  902. static void
  903. format_xlations(String s, FILE *f)
  904. {
  905.     char *t;
  906.     char *t_next;
  907.     struct xl {
  908.         struct xl *next;
  909.         char *actions;
  910.         char *event;
  911.         char *keymap;
  912.         int km_line;
  913.         char *full_keymap;
  914.     } *xl_list = (struct xl *)NULL, *x, *xs, *xlp, *xn;
  915.     char *km_last;
  916.     int line_last = 0;
  917.  
  918.     /* Construct the list. */
  919.     for (t = s; t != CN; t = t_next) {
  920.         char *k, *a, *kk;
  921.         int nq;
  922.         static char cmps[] = ": " PA_KEYMAP_TRACE "(";
  923.  
  924.         /* Find the end of this rule and terminate this line. */
  925.         t_next = strstr(t, PA_ENDL "\n");
  926.         if (t_next != CN) {
  927.             t_next += strlen(PA_ENDL);
  928.             *t_next++ = '\0';
  929.         }
  930.  
  931.         /* Remove the leading space. */
  932.         while (*t == ' ')
  933.             t++;
  934.  
  935.         /* Use only traced events. */
  936.         k = strstr(t, cmps);
  937.         if (k == CN)
  938.             continue;
  939.         *k = '\0';
  940.         k += strlen(cmps);
  941.  
  942.         /* Find the rest of the actions. */
  943.         a = strchr(k, ')');
  944.         if (a == CN)
  945.             continue;
  946.         while (*(++a) == ' ')
  947.             ;
  948.         if (!*a)
  949.             continue;
  950.  
  951.         /* Remove the trailing PA-End call. */
  952.         if (strlen(a) >= strlen(PA_ENDL) &&
  953.             !strcmp(a + strlen(a) - strlen(PA_ENDL), PA_ENDL))
  954.             a[strlen(a) - strlen(PA_ENDL)] = '\0';
  955.  
  956.         /* Allocate the new element. */
  957.         x = (struct xl *)XtCalloc(sizeof(struct xl), 1);
  958.         x->actions = XtNewString(a);
  959.         x->event = XtNewString(t);
  960.         x->keymap = kk = (char *)XtMalloc(a - k + 1);
  961.         x->km_line = 0;
  962.         x->full_keymap = (char *)XtMalloc(a - k + 1);
  963.         nq = 0;
  964.         while (*k != ')') {
  965.             if (*k == '"') {
  966.                 nq++;
  967.             } else if (nq == 1) {
  968.                 *kk++ = *k;
  969.             } else if (nq == 3) {
  970.                 x->km_line = atoi(k);
  971.                 break;
  972.             }
  973.             k++;
  974.         }
  975.         *kk = '\0';
  976.         (void) sprintf(x->full_keymap, "%s:%d", x->keymap, x->km_line);
  977.  
  978.         /* Find where it should be inserted. */
  979.         for (xs = xl_list, xlp = (struct xl *)NULL;
  980.              xs != (struct xl *)NULL;
  981.              xlp = xs, xs = xs->next) {
  982.             int halt = 0;
  983.  
  984.             switch (sort) {
  985.                 case SORT_EVENT:
  986.                 halt = (event_cmp(xs->event, x->event) > 0);
  987.                 break;
  988.                 case SORT_KEYMAP:
  989.                 halt = (keymap_cmp(xs->keymap, xs->km_line,
  990.                            x->keymap, x->km_line) > 0);
  991.                 break;
  992.                 case SORT_ACTION:
  993.                 halt = (action_cmp(xs->actions, a) > 0);
  994.                 break;
  995.             }
  996.             if (halt)
  997.                 break;
  998.         }
  999.  
  1000.         /* Insert it. */
  1001.         if (xlp != (struct xl *)NULL) {
  1002.             x->next = xlp->next;
  1003.             xlp->next = x;
  1004.         } else {
  1005.             x->next = xl_list;
  1006.             xl_list = x;
  1007.         }
  1008.     }
  1009.  
  1010.     /* Walk it. */
  1011.     if (sort != SORT_KEYMAP)
  1012.         (void) fprintf(f, "%-26s %-16s %s\n%s\n",
  1013.             get_message("kmEvent"),
  1014.             get_message("kmKeymapLine"),
  1015.             get_message("kmActions"),
  1016.             DASHES);
  1017.     km_last = CN;
  1018.     for (xs = xl_list; xs != (struct xl *)NULL; xs = xs->next) {
  1019.         switch (sort) {
  1020.             case SORT_EVENT:
  1021.             if (km_last != CN) {
  1022.                 char *l;
  1023.  
  1024.                 l = strchr(xs->event, '<');
  1025.                 if (l != CN) {
  1026.                     if (strcmp(km_last, l))
  1027.                         (void) fprintf(f, "\n");
  1028.                     km_last = l;
  1029.                 }
  1030.             } else
  1031.                 km_last = strchr(xs->event, '<');
  1032.             break;
  1033.             case SORT_KEYMAP:
  1034.             if (km_last == CN || strcmp(xs->keymap, km_last)) {
  1035.                 char *p;
  1036.  
  1037.                 (void) fprintf(f, "%s%s '%s'%s",
  1038.                     km_last == CN ? "" : "\n",
  1039.                     get_message(is_temp(xs->keymap) ?
  1040.                             "kmTemporaryKeymap" :
  1041.                             "kmKeymap"),
  1042.                     xs->keymap,
  1043.                     from_server(xs->keymap) ?
  1044.                     get_message("kmFromServer") : "");
  1045.                 if ((p = pathname(xs->keymap)) != CN)
  1046.                     (void) fprintf(f, ", %s %s",
  1047.                         get_message("kmFile"), p);
  1048.                 else
  1049.                     (void) fprintf(f,
  1050.                         ", %s %s.%s.%s",
  1051.                         get_message("kmResource"),
  1052.                         programname, ResKeymap, xs->keymap);
  1053.                 (void) fprintf(f,
  1054.                     "\n%-26s %-16s %s\n%s\n",
  1055.                     get_message("kmEvent"),
  1056.                     get_message("kmKeymapLine"),
  1057.                     get_message("kmActions"),
  1058.                     DASHES);
  1059.                 km_last = xs->keymap;
  1060.                 line_last = 0;
  1061.             }
  1062.             while (xs->km_line != ++line_last) {
  1063.                 (void) fprintf(f, "%-26s %s:%d\n",
  1064.                     get_message("kmOverridden"),
  1065.                     xs->keymap, line_last);
  1066.             }
  1067.             break;
  1068.             case SORT_ACTION:
  1069.             break;
  1070.         }
  1071.         (void) fprintf(f, "%-26s %-16s ",
  1072.             xs->event, xs->full_keymap);
  1073.         (void) fcatv(f, xs->actions);
  1074.         (void) fputc('\n', f);
  1075.     }
  1076.  
  1077.     /* Free it. */
  1078.     for (xs = xl_list; xs != (struct xl *)NULL; xs = xn) {
  1079.         xn = xs->next;
  1080.         XtFree((XtPointer)xs->actions);
  1081.         XtFree((XtPointer)xs->event);
  1082.         XtFree((XtPointer)xs->keymap);
  1083.         XtFree((XtPointer)xs->full_keymap);
  1084.         XtFree((XtPointer)xs);
  1085.     }
  1086. }
  1087.  
  1088. /*
  1089.  * Comparison function for actions.  Basically, a strcmp() that handles "PF"
  1090.  * and "PA" specially.
  1091.  */
  1092. #define PA    "PA("
  1093. #define PF    "PF("
  1094. static int
  1095. action_cmp(char *s1, char *s2)
  1096. {
  1097.     if ((!strncmp(s1, PA, 3) && !strncmp(s2, PA, 3)) ||
  1098.         (!strncmp(s1, PF, 3) && !strncmp(s2, PF, 3)))
  1099.         return atoi(s1 + 4) - atoi(s2 + 4);
  1100.     else
  1101.         return strcmp(s1, s2);
  1102.         
  1103. }
  1104.  
  1105. /* Return a keymap's index in the lists. */
  1106. static int
  1107. km_index(char *n)
  1108. {
  1109.     struct trans_list *t;
  1110.     int ix = 0;
  1111.  
  1112.     for (t = trans_list; t != (struct trans_list *)NULL; t = t->next) {
  1113.         if (!strcmp(t->name, n))
  1114.             return ix;
  1115.         ix++;
  1116.     }
  1117.     for (t = temp_keymaps; t != (struct trans_list *)NULL; t = t->next) {
  1118.         if (!strcmp(t->name, n))
  1119.             return ix;
  1120.         ix++;
  1121.     }
  1122.     return ix;
  1123. }
  1124.  
  1125. /* Return whether or not a keymap is temporary. */
  1126. static Boolean
  1127. is_temp(char *k)
  1128. {
  1129.     struct trans_list *t;
  1130.  
  1131.     for (t = trans_list; t != (struct trans_list *)NULL; t = t->next) {
  1132.         if (!strcmp(t->name, k))
  1133.             return t->is_temp;
  1134.     }
  1135.     for (t = temp_keymaps; t != (struct trans_list *)NULL; t = t->next) {
  1136.         if (!strcmp(t->name, k))
  1137.             return t->is_temp;
  1138.     }
  1139.     return False;
  1140. }
  1141.  
  1142. /* Return the pathname associated with a keymap. */
  1143. static char *
  1144. pathname(char *k)
  1145. {
  1146.     struct trans_list *t;
  1147.  
  1148.     for (t = trans_list; t != (struct trans_list *)NULL; t = t->next) {
  1149.         if (!strcmp(t->name, k))
  1150.             return t->pathname;
  1151.     }
  1152.     for (t = temp_keymaps; t != (struct trans_list *)NULL; t = t->next) {
  1153.         if (!strcmp(t->name, k))
  1154.             return t->pathname;
  1155.     }
  1156.     return CN;
  1157. }
  1158.  
  1159. /* Return whether or not a keymap was translated from "@server". */
  1160. static Boolean
  1161. from_server(char *k)
  1162. {
  1163.     struct trans_list *t;
  1164.  
  1165.     for (t = trans_list; t != (struct trans_list *)NULL; t = t->next) {
  1166.         if (!strcmp(t->name, k))
  1167.             return t->from_server;
  1168.     }
  1169.     for (t = temp_keymaps; t != (struct trans_list *)NULL; t = t->next) {
  1170.         if (!strcmp(t->name, k))
  1171.             return t->from_server;
  1172.     }
  1173.     return False;
  1174. }
  1175.  
  1176. /*
  1177.  * Comparison function for keymaps.
  1178.  */
  1179. static int
  1180. keymap_cmp(char *k1, int l1, char *k2, int l2)
  1181. {
  1182.     /* If the keymaps are the same, do a numerical comparison. */
  1183.     if (!strcmp(k1, k2))
  1184.         return l1 - l2;
  1185.  
  1186.  
  1187.     /* The keymaps are different.  Order them according to trans_list. */
  1188.     return km_index(k1) - km_index(k2);
  1189. }
  1190.  
  1191. /*
  1192.  * strcmp() that handles <KeyPress>Fnn numercally.
  1193.  */
  1194. static int
  1195. Fnn_strcmp(char *s1, char *s2)
  1196. {
  1197.     static char kp[] = "<KeyPress>F";
  1198. #    define KPL (sizeof(kp) - 1)
  1199.  
  1200.     if (strncmp(s1, kp, KPL) || !isdigit(s1[KPL]) ||
  1201.         strncmp(s2, kp, KPL) || !isdigit(s2[KPL]))
  1202.         return strcmp(s1, s2);
  1203.     return atoi(s1 + KPL) - atoi(s2 + KPL);
  1204. }
  1205.  
  1206. /*
  1207.  * Comparison function for events.
  1208.  */
  1209. static int
  1210. event_cmp(char *e1, char *e2)
  1211. {
  1212.     char *l1, *l2;
  1213.     int r;
  1214.  
  1215.     /* If either has a syntax problem, do a straight string compare. */
  1216.     if ((l1 = strchr(e1, '<')) == CN ||
  1217.         (l2 = strchr(e2, '<')) == CN)
  1218.         return strcmp(e1, e2);
  1219.  
  1220.     /*
  1221.      * If the events are different, sort on the event only.  Otherwise,
  1222.      * sort on the modifier(s).
  1223.      */
  1224.     r = Fnn_strcmp(l1, l2);
  1225.     if (r)
  1226.         return r;
  1227.     else
  1228.         return strcmp(e1, e2);
  1229. }
  1230. #endif /*]*/
  1231.